////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2003-2006 Adobe Macromedia Software LLC and its licensors.
//  All Rights Reserved. The following is Source Code and is subject to all
//  restrictions on such code as contained in the End User License Agreement
//  accompanying this product.
//
////////////////////////////////////////////////////////////////////////////////

package mriquery.componentes.mascaras
{
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TextEvent;
	import flash.text.TextFormat;
	import flash.ui.Keyboard;
	import flash.utils.getTimer;
	
	import mx.controls.TextArea;
	import mx.controls.TextInput;
	import mx.core.EdgeMetrics;
	import mx.core.IFlexDisplayObject;
	import mx.core.IInvalidating;
	import mx.core.UIComponent;
	import mx.core.UITextField;
	import mx.core.mx_internal;
	import mx.skins.ProgrammaticSkin;
	import mx.skins.RectangularBorder;
	import mx.styles.ISimpleStyleClient;
	import mriquery.componentes.mascaras.MTISkin;
	
	use namespace mx_internal;
	
//--------------------------------------
//  Events
//--------------------------------------
	/**
	 *  Dispatched when the last character is entered in the
	 *  Masked Text Input control through user input.
	 *
	 *  @eventType flash.events.Event
	 *  @tiptext inputMaskEnd event
	 */
	[Event(name="inputMaskEnd", type="flash.events.Event")]
//--------------------------------------
//  Styles
//--------------------------------------
	/**
	 * The color of the background cell.
	 *  @default 0xFF0000 if required property is set, 0x008CEA otherwise
	 */
	[Style(name="cellColor", type="uint", format="Color", inherit="no")]
	/**
	 * The color of the text when  required property is set,
	 *  and the field is left incomplete.
	 *  @default 0xFF0000
	 */
	[Style(name="errorTextColor", type="uint", format="Color", inherit="no")]
//--------------------------------------
//  Skins
//--------------------------------------
	/**
	 *  Name of the class to use as the skin for the background.
	 *
	 *  @default "com.adobe.flex.extras.controls.skins.MTISkin"
	 */
	[Style(name="MTISkin", type="Class", inherit="no")]
	/**
	 * 	The Masked Text Input component is a single-line,
	 * 	text input field. This component adds support for
	 * 	the validation of input against a specified mask expression.
	 * 	For using the control, input mask should be provided
	 * 	by setting the <code>inputMask</code> property of the
	 * 	control. This control shows meaningful behavior only
	 * 	when <code>inputMask</code> is specified in a correct manner.
	 *
	 * 	<p>If the mask is not specified correctly or it is
	 * 	not provided, then the control will work as a
	 * 	standard Text Input control.
	 *
	 * 	@mxml
	 *
	 * 	<p>The <code>&lt;mx:MaskedTextInput&gt;</code> tag inherits the attributes
	 * 	of its superclass and adds the following attributes:</p>
	 *
	 *  <pre>
	 *  &lt;fc:MaskedTextInput
	 *    <b>Properties</b>
	 *    inputMask=""
	 * 	  text=""
	 * 	  fullText=""
	 * 	  defaultCharacter="_"
	 * 	  required=false
	 * 	  autoAdvance=false
	 * 	  &nbsp;
	 *    <b>Styles</b>
	 *    cellColor="0x008CEA"
	 * 	  errorTextColor="0xFF0000"
	 * 	  &nbsp;
	 *    <b>Events</b>
	 * 	  inputMaskEnd="<i>No default</i>"
	 *  /&gt;
	 *  </pre>
	 *
	 *  @includeExample ../../../../../../docs/com/adobe/flex/extras/controls/example/MaskedTextInputExample/MaskedTextInputExample.mxml
	 *
	 *  @see mx.controls.TextInput
	 *
	 *  @tiptext TextInput is a single-line, editable text field.
	 *
	 */
	public class MaskedTextInput extends TextInput
	{
		
		//------------------------------------------------------------------------------
		//
		//   Attributos 
		//
		//------------------------------------------------------------------------------
		
		//--------------------------------------
		//   Property 
		//--------------------------------------
		
		/**
		 *  Internal array to store the data in the Text Field.
		 */
		mx_internal var _working:Array = [];
		
		/**
		 *  @private
		 *  Skin names.
		 *  Allows subclasses to re-define the skin property names.
		 */
		mx_internal var backgroundSkinName:String = "MTISkin";
		
		/**
		 *  Internal array to store each character in the mask
		 *  and whether it is to be entered by the user or displayed as it is.
		 */
		mx_internal var maskMap:Array;
		
		/**
		 *  @private
		 *  Storage for Escape character.
		 */
		private const ESCAPE:String = "/";
		
		/**
		 *  @private
		 *  Storage for the autoAdvance property.
		 */
		private var _autoAdvance:Boolean = false;
		
		/**
		 *  @private
		 *  Storage for the defaultCharacter property.
		 */
		private var _defaultCharacter:String = "_";
		
		/**
		 *  @private
		 *  Storage for the input mask.
		 */
		private var _inputMask:String = "";
		
		/**
		 *  @private
		 *  Storage for current cursor position.
		 */
		private var _position:Number = 0;
		
		/**
		 *  @private
		 *  Storage for the required property.
		 */
		private var _required:Boolean = false;
		
		/**
		 *  @private
		 *  Storage for the text property.
		 */
		private var _text:String = "";
		
		/**
		 *  @private
		 *  Storage for the length of the Text Field.
		 */
		private var actualLength:Number = 0;
		
		/**
		 *  @private
		 *  Specifies that the field is a date only field.
		 */
		private var dateField:Boolean = false;
		
		/**
		 *  @private
		 *  Flag set when either the mask is entered wrongly or is not specified.
		 *  The control will work as a standard Text Input if defaultBehaviour is set
		 */
		private var defaultBehaviour:Boolean = true;
		
		/**
		 *  @private
		 *  The String to be shown as the embeded hint.
		 */
		private var embedStr:String = "";
		
		//--------------------------------------------------------------------------
		//
		//  Variables
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 * The UITextField control for displaying embeded hints.
		 */
		private var embedTextField:UITextField;
		
		/**
		 *  @private
		 *  Flag set when focus is set by using mouse click.
		 */
		private var focusByClick:Boolean = false;
		
		/**
		 *  @private
		 *  Flag set when font-family/font-size is updated.
		 */
		private var fontUpdated:Boolean = true;
		
		/**
		 *  @private
		 *  Storage for the time when double click event happened.
		 */
		private var lastTime:Number = 0;
		
		/**
		 *  @private
		 *  Flag set when inputMask is updated.
		 */
		private var maskUpdated:Boolean = false;
		
		/**
		 *  @private
		 *  Flag set when text is updated.
		 */
		private var textUpdated:Boolean = false;
		
		/**
		 *  @private
		 *  Flag set when _working is updated.
		 */
		private var workingUpdated:Boolean = false;
		
		//------------------------------------------------------------------------------
		//
		//   Metodos 
		//
		//------------------------------------------------------------------------------
		
		//--------------------------------------
		//   Constructor 
		//--------------------------------------
		
		/**
		 *  Constructor.
		 */
		public function MaskedTextInput()
		{
			super();
			
			// Adding Listeners for various events
			addEventListener(TextEvent.TEXT_INPUT, interceptChar, true, 0);
			addEventListener(MouseEvent.CLICK, reposition, true);
			addEventListener(MouseEvent.MOUSE_DOWN, reposition, true);
			addEventListener(KeyboardEvent.KEY_DOWN, interceptKey, true);
			addEventListener(FocusEvent.FOCUS_IN, interceptFocus, false);
			addEventListener(FocusEvent.FOCUS_OUT, interceptFocus);
			addEventListener(Event.CHANGE, menuHandler);
			
			// commented to retain the default Tab and Shift-Tab behavior
			// uncomment if you want the focus to be shifted to the
			// next/previous sub-field in case of a Tab/Shift-Tab respectively.
			//addEventListener(FocusEvent.KEY_FOCUS_CHANGE,tabHandler);
			
			
			this.doubleClickEnabled = true;
			addEventListener(MouseEvent.DOUBLE_CLICK, handleDoubleClick, true);
		
		}
		
		//--------------------------------------
		//   Function 
		//--------------------------------------
		
		public function get autoAdvance():Boolean
		{
			return _autoAdvance;
		}
		
		[Bindable("change")]
		[Inspectable(category="General", defaultValue="false")]
		/**
		 *  If set to true, focus will automatically move to the next field
		 *  in the tab order.
		 *
		 *  @default false
		 */
		public function set autoAdvance(value:Boolean):void
		{
			_autoAdvance = value;
		}
		
		public function get defaultCharacter():String
		{
			return _defaultCharacter;
		}
		
		[Bindable("change")]
		[Inspectable(category="General", defaultValue="_")]
		/**
		 *  The character used in place of an empty space
		 *  when fullText is returned.
		 *
		 *  @default "_"
		 */
		public function set defaultCharacter(str:String):void
		{
			_defaultCharacter = str.charAt(0);
		}
		
		[Bindable("change")]
		[Bindable("valueCommit")]
		[Inspectable(category="General", defaultValue="")]
		/**
		 *  Returns the whole text displayed in the Text Field.
		 */
		public function get fullText():String
		{
			if (defaultBehaviour)
			{
				return super.text;
			}
			
			var result:String = "";
			
			for (var i:Number = 0; i < _working.length; i++)
			{
				var c:String = _working[i];
				
				if (c == " ")
				{
					c = _defaultCharacter;
				}
				result += c;
			}
			return result;
		}
		
		/**
		 *  @private
		 */
		override public function getStyle(styleProp:String):*
		{
			if (styleProp == "cellColor")
			{
				if (!super.getStyle("cellColor"))
				{
					if (required)
					{
						return 0xFFFFFF;
					}
					else
					{
						return 0xFFFFFF;
					}
				}
			}
			
			if (styleProp == "errorTextColor")
			{
				if (!super.getStyle("errorTextColor"))
				{
					return 0xFF0000;
				}
			}
			
			return super.getStyle(styleProp);
		}
		
		[Bindable("change")]
		[Inspectable(category="General", defaultValue="")]
		/**
		 *  The input mask used for validating the input.
		 *
		 */
		public function get inputMask():String
		{
			return _inputMask;
		}
		
		public function set inputMask(s:String):void
		{
			maskMap = [];
			_inputMask = s;
			dateField = false;
			
			switch (_inputMask)
			{
				case "MM/DD/YYYY":
				{
					_inputMask = "##//##//####";
					embedStr = "MM DD YYYY";
					dateField = true;
					break;
				}
				case "DD/MM/YYYY":
				{
					_inputMask = "##//##//####";
					embedStr = "DD MM YYYY";
					dateField = true;
					break;
				}
				case "YYYY/MM/DD":
				{
					_inputMask = "####//##//##";
					embedStr = "YYYY MM DD";
					dateField = true;
					break;
				}
				case "YYYY/DD/MM":
				{
					_inputMask = "####//##//##";
					embedStr = "YYYY DD MM";
					dateField = true;
					break;
				}
				default:
					break;
			}
			
			defaultBehaviour = false;
			checkMask();
			actualLength = calculateMaxChars();
			
			if (!defaultBehaviour)
			{
				maxChars = actualLength;
			}
			maskUpdated = true;
			invalidateDisplayList();
		}
		
		public function get required():Boolean
		{
			return _required;
		}
		
		[Bindable("change")]
		[Inspectable(category="General", defaultValue="false")]
		/**
		 *  If set and if the field is left incomplete,
		 *  the color of the characters in the field will be changed
		 *  to errorTextColor or to red if errorTextColor is not specified,
		 *
		 *  @default false
		 */
		public function set required(value:Boolean):void
		{
			_required = value;
			// when required is changed dynamically, 
			// then the change should be reflected.
			invalidateDisplayList();
		}
		
		/**
		 *  @private
		 */
		override public function styleChanged(styleProp:String):void
		{
			super.styleChanged(styleProp);
			
			// using date mask with validators changes the text color to black,
			// in case of a validation failure.
			// setting the color of the embeded text field to white.
			if (styleProp == "themeColor")
			{
				if (embedTextField)
				{
					embedTextField.setColor(0xFFFFFF);
				}
			}
			
			if (styleProp == "fontFamily" || styleProp == "fontSize")
			{
				fontUpdated = true;
				measuredMinHeight = measureText("Wj").height + 4;
				measuredHeight = measuredMinHeight;
				invalidateDisplayList();
				
			}
		}
		
		[Bindable("change")]
		[Bindable("valueCommit")]
		[Inspectable(category="General", defaultValue="")]
		
		/**
		 *  Returns only the text entered by the user.
		 */
		override public function get text():String
		{
			if (defaultBehaviour)
			{
				return super.text;
			}
			
			var result:String = "";
			
			for (var i:Number = 0; i < _working.length; i++)
			{
				var c:String = _working[i];
				
				if (!maskMap[i][1])
				{
					continue;
				}
				else if (c == " ")
				{
					c = "";
				}
				result += c;
			}
			return result;
		}
		
		override public function set text(value:String):void
		{
			if (defaultBehaviour)
			{
				super.text = value;
				//invalidateDisplayList();
				return;
			}
			_text = value;
			textUpdated = true;
			invalidateDisplayList();
		}
		
		/**
		 *  @private
		 *  Create child objects for displaying embeded hints.
		 */
		override protected function createChildren():void
		{
			super.createChildren();
			
			if (dateField)
			{
				embedTextField = new UITextField();
				embedTextField.text = embedStr;
				addChildAt(embedTextField, getChildIndex(textField as DisplayObject));
			}
		}
		
		/**
		 *  @private
		 */
		override protected function measure():void
		{
			super.measure();
			var bm:EdgeMetrics = border && border is RectangularBorder ? RectangularBorder(border).borderMetrics : EdgeMetrics.EMPTY;
			
			measuredMinHeight = measureText("Wj").height + 4 + bm.top + bm.bottom;
			measuredHeight = measuredMinHeight;
		}
		
		/**
		 *  @private
		 *  Modifies the display according to how flags are set: if
		 *  text has been updated, fold the text according to the mask. If
		 *  the mask has been updated, modify the display.
		 */
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			if (defaultBehaviour)
			{
				defaultBehaviour = true;
				super.updateDisplayList(unscaledWidth, unscaledHeight);
				return;
			}
			
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			
			if (maskUpdated)
			{
				maskUpdated = false;
				
				_working = [];
				
				for (var i:int = 0; i < actualLength; i++)
				{
					var c:String = " ";
					
					if (!maskMap[i][1])
					{
						c = maskMap[i][0];
					}
					_working.push(c);
					
				}
				
				//width = measureText("W").width * actualLength + 2 * getStyle("borderThickness") + 5;
				workingUpdated = true;
			}
			
			if (textUpdated)
			{
				textUpdated = false;
				_working = [];
				
				for (var j:int = 0; j < actualLength; j++)
				{
					var ch:String = " ";
					
					if (!maskMap[j][1])
					{
						ch = maskMap[j][0];
					}
					_working.push(ch);
					
				}
				_position = 0;
				handleInput(_text);
				workingUpdated = true;
			}
			
			if (workingUpdated)
			{
				super.text = _working.join("");
				workingUpdated = false;
			}
			
			if (fontUpdated)
			{
				fontUpdated = false;
				
				// Set the font size to 72 if user has specified the
				// font size to be greater than 72
				/* if (getStyle("fontSize") > 72)
				{
					//setStyle("fontSize", 72);
				}
				
				// Check if Monospaced font is used or not
				// Set the font to Courier if font used is other then a monospaced font
				if (measureText("W").width != measureText("I").width)
				{
					//setStyle("fontFamily", "Courier");
				} */
				
				// Set the width of the control
				//width = measureText("W").width * actualLength + 2 * getStyle("borderThickness") + 5;
			}
			
			if (dateField)
			{
				// when inputMask is changed dynamically, 
				// then in case of a date mask,
				// create the embeded text field for showing
				// embeded hints if its not already created.
				// If it is created, then just change the embeded hint.
				if (!embedTextField)
				{
					embedTextField = new UITextField();
					addChildAt(embedTextField, getChildIndex(textField as DisplayObject));
				}
				embedTextField.text = embedStr;
				
				embedTextField.alpha = 1;
				
				var txtFormat:TextFormat = new TextFormat();
				txtFormat.color = 0xFFFFFF;
				embedTextField.setTextFormat(txtFormat);
				embedTextField.x = textField.x;
				embedTextField.y = textField.y;
				embedTextField.setActualSize(width, height);
			}
			else
			{
				if (embedTextField)
				{
					embedTextField.text = "";
					embedTextField = null;
				}
			}
			
			// create or updates the control's skin
			createSkin();
			
			textField.width += 50;
		}
		
		/**
		 *  @private
		 *  Moves the insertion point forward (if possible) to the next viable
		 *  input position.
		 *  byArrow denotes that advancePosition is called when
		 *  the user has pressed Arrow key or not.
		 */
		private function advancePosition(byArrow:Boolean = false):void
		{
			var p:Number = _position;
			
			var posChanged:Boolean = false;
			
			while ((++p) < actualLength)
			{
				posChanged = true;
				
				if (p >= actualLength - 1)
				{
					p = actualLength - 1;
					break;
				}
				
				if (maskMap[p][1])
				{
					break;
				}
				
				
			}
			
			if (posChanged || p == actualLength)
			{
				_position = p;
			}
			
			// byArrow denotes that advancePosition is called when
			// the user has pressed Arrow key or not
			if (p >= actualLength && !byArrow)
			{
				if (_autoAdvance && !isWorkingIncomplete())
				{
					// Going to the next field
					var obj:UIComponent = UIComponent(focusManager.getNextFocusManagerComponent());
					
					if ((obj is MaskedTextInput) || (obj is TextInput) || (obj is TextArea))
					{
						obj.setFocus();
					}
					
				}
				dispatchEvent(new Event("inputMaskEnd"));
			}
			setSelection(_position, _position);
		}
		
		/**
		 *  @private
		 *  Inserts the character into the working array.
		 */
		private function allowChar(c:String):void
		{
			// Commented so that the characeters dont shift to their left when deleting
			/* var insertionPossible:Boolean = false;
			   var i:int = _position;
			   while((i+1) <= actualLength && maskMap[i][1])
			   {
			   if(_working[i] == " " && !(maskMap[i][0] == " "))
			   {
			   insertionPossible = true;
			   break;
			   }
			   i++;
			   }
			   while(insertionPossible && (i) > _position && maskMap[i][1])
			   {
			   _working[i] = _working[i-1];
			   i--;
			 } */
			_working[_position] = c;
		}
		
		/**
		 *  @private
		 *  Calculate the number of characters which are displayed
		 *  (i.e., number of character in the inputMask excluding
		 *  the ESCAPE character) and can be entered in the text field.
		 *  Also populate the array maskMap which specifies that
		 *  a character at a particular position is to be
		 *  entered by the user or displayed as it is.
		 */
		private function calculateMaxChars():Number
		{
			maskMap = new Array();
			var count:Number = 0;
			
			for (var i:int = 0, k:int = 0; i < _inputMask.length; i++, k++)
			{
				if ((_inputMask.charAt(i) == ESCAPE && _inputMask.charAt(i + 1) == ESCAPE) || (_inputMask.charAt(i) == ESCAPE && isMask(_inputMask.charAt(i + 1))))
				{
					maskMap[k] = [ _inputMask.charAt(i + 1), false ];
					i++;
				}
				else if (isMask(_inputMask.charAt(i)))
				{
					maskMap[k] = [ _inputMask.charAt(i), true ];
				}
				else
				{
					maskMap[k] = [ _inputMask.charAt(i), false ];
				}
				count++;
			}
			return count;
		}
		
		/**
		 *  @private
		 *  Change the selected text to select the text in the current subfield.
		 */
		
		private function changeSelection(pos:Number):void
		{
			var startPos:int = pos;
			
			while (!maskMap[startPos][1])
			{
				startPos++;
			}
			
			var endPos:int = startPos;
			
			while (endPos < actualLength && maskMap[endPos][1] && _working[endPos] != " ")
			{
				endPos++;
			}
			setSelection(startPos, endPos);
		}
		
		/**
		 *  @private
		 *  Check for the validity of the mask specified.
		 *  If the mask is not valid then set the flag defaultBehaviour to true.
		 *  If flag defaultBehaviour is set then the Masked Text Input Control will
		 *  function as a standard Text Input control.
		 */
		private function checkMask():void
		{
			if (!_inputMask || _inputMask == "")
			{
				defaultBehaviour = true;
				return;
			}
			
			for (var i:int = 0; i < _inputMask.length; i++)
			{
				if ((_inputMask.charAt(i) == ESCAPE && _inputMask.charAt(i + 1) == ESCAPE) || (_inputMask.charAt(i) == ESCAPE && isMask(_inputMask.charAt(i + 1))))
				{
					i++;
					continue;
				}
				else if (_inputMask.charAt(i) == ESCAPE && (_inputMask.charAt(i + 1) != ESCAPE || !isMask(_inputMask.charAt(i + 1))))
				{
					defaultBehaviour = true;
					break;
				}
				else if (isMask(_inputMask.charAt(i)))
				{
					continue;
				}
			}
		}
		
		/**
		 *  @private
		 */
		private function createSkin():void
		{
			var skinName:String = backgroundSkinName;
			
			// Has this skin already been created?
			var newSkin:IFlexDisplayObject = IFlexDisplayObject(getChildByName(skinName));
			
			// If not, create it.
			if (!newSkin)
			{
				var newSkinClass:Class = Class(getStyle(skinName));
				
				if (!newSkinClass)
				{
					newSkinClass = MTISkin;
				}
				
				if (newSkinClass)
				{
					newSkin = IFlexDisplayObject(new newSkinClass());
					
					// Set its name so that we can find it in the future
					// using getChildByName().
					newSkin.name = skinName;
					
					// Make the getStyle() calls in MTISkin find the styles
					// for this control.
					var styleableSkin:ISimpleStyleClient = newSkin as ISimpleStyleClient;
					
					if (styleableSkin)
					{
						styleableSkin.styleName = this;
					}
					
					if (embedTextField)
					{
						addChildAt(DisplayObject(newSkin), getChildIndex(embedTextField));
					}
					else
					{
						addChild(DisplayObject(newSkin));
					}
					
					// If the skin is programmatic, and we've already been
					// initialized, update it now to avoid flicker.
					if (newSkin is IInvalidating && initialized)
					{
						IInvalidating(newSkin).validateNow();
					}
					else if (newSkin is ProgrammaticSkin && initialized)
					{
						ProgrammaticSkin(newSkin).invalidateDisplayList();
					}
				}
			}
			// If the skin is already created then redraw it
			// depending on the characters entered in the text input
			else
			{
				ProgrammaticSkin(newSkin).invalidateDisplayList();
			}
		}
		
		/**
		 *  @private
		 *  Handles Deletion of characters caused by pressing
		 *  backspace/Delete key.
		 */
		private function handleDeletions(backSpace:Boolean = false):void
		{
			var i:int = 0;
			var s:String = "";
			
			if (this.selectionBeginIndex == this.selectionEndIndex || this.selectionBeginIndex == this.selectionEndIndex + 1)
			{
				var startIndex:int = -1;
				var endIndex:int = actualLength;
				
				if (backSpace)
				{
					startIndex = 0;
					endIndex = actualLength + 1;
				}
				
				if (_position > startIndex && _position < endIndex)
				{
					if (backSpace)
					{
						retreatPosition();
					}
					
					i = _position;
					
					if (!maskMap[_position][1])
					{
						_working[_position] = maskMap[_position][0];
					}
					else
					{
						// Commented so that characters dont shift to left when deleting
						/* while((i+1) < actualLength && _working[i+1] != " " && maskMap[i+1][1])
						   {
						   _working[i] = _working[i+1];
						   i++;
						 } */
						_working[i] = " ";
						
						// Added so that delete can work fine.
						if (!backSpace && _position < actualLength - 1 && _working[_position + 1] != " ")
						{
							advancePosition();
								//setSelection(_position,_position);
						}
						
						if (dateField)
						{
							s = embedTextField.text == null ? "" : embedTextField.text;
							
							if (s.length > 0)
							{
								s = s.substring(0, _position) + " " + s.substring(_position + 1, s.length);
								s = s.substring(0, i) + embedStr.charAt(i) + s.substring(i + 1, s.length);
								embedTextField.text = s;
							}
						}
					}
				}
			}
			else
			{
				_position = this.selectionBeginIndex;
				i = _position - 1;
				
				for (var j:int = this.selectionBeginIndex; j < this.selectionEndIndex; j++)
				{
					// Commented so that characters dont shift to left when deleting
					//i = _position;
					//if(!maskMap[_position][1])
					//	_working[_position] = maskMap[_position][0];
					i++;
					
					if (!maskMap[i][1])
					{
						_working[i] = maskMap[i][0];
					}
					else
					{
						// Commented so that characters dont shift to left when deleting
						/* while((i+1) < actualLength && _working[i+1] != " " && maskMap[i+1][1])
						   {
						   _working[i] = _working[i+1];
						   i++;
						 } */
						_working[i] = " ";
						
						if (dateField)
						{
							s = embedTextField.text == null ? "" : embedTextField.text;
							
							if (s.length > 0)
							{
								// Commented so that characters dont shift to left when deleting
								//s = s.substring(0,_position) + " " + s.substring(_position+1, s.length);
								//s = s.substring(0,i) + embedStr.charAt(i) + s.substring(i+1, s.length);
								//embedTextField.text = s;
								
								s = s.substring(0, i) + " " + s.substring(i + 1, s.length);
								s = s.substring(0, i) + embedStr.charAt(i) + s.substring(i + 1, s.length);
								embedTextField.text = s;
							}
						}
					}
				}
			}
		}
		
		/**
		 *  @private
		 *  Handles MOUSE_DOUBLECLICK event and selects the sub field.
		 */
		private function handleDoubleClick(event:MouseEvent):void
		{
			if (defaultBehaviour)
			{
				return;
			}
			
			textField.selectable = false;
			lastTime = getTimer();
			
			var startPos:int = textField.getCharIndexAtPoint(event.localX, event.localY) != -1 ? textField.getCharIndexAtPoint(event.localX, event.localY) : (event.localX < 10 ? 0 : (actualLength - 1));
			
			while (startPos > 0 && startPos < actualLength && maskMap[startPos - 1][1])
			{
				startPos--;
			}
			
			if (!isWorkingIncomplete(startPos))
			{
				changeSelection(startPos);
			}
			
			event.stopImmediatePropagation();
			event.preventDefault();
			
			textField.selectable = true;
			
			return;
		}
		
		/**
		 *  @private
		 *  Handle the text entered by the user or specified in the
		 *  text property.
		 */
		private function handleInput(input:String, event:Event = null):void
		{
			if (input)
			{
				for (var i:int = 0; i < input.length; i++)
				{
					
					var c:String = input.charAt(i);
					var m:String;
					
					if (_position >= actualLength)
					{
						return;
					}
					
					var pos:Number = (_position < 0) ? 0 : ((_position >= actualLength) ? actualLength - 1 : _position);
					
					if (pos < actualLength && maskMap[pos][0] != null)
					{
						m = maskMap[pos][0];
					}
					else
					{
						if (event != null)
						{
							event.preventDefault();
						}
						return;
					}
					
					// Flag set to false if the character is not accepted
					var bAdvance:Boolean = true;
					
					// Check the character entered with the mask character at that position
					switch (m)
					{
						case "#":
						{
							// if the character is a digit, accept it
							if (isDigit(c))
							{
								allowChar(c);
							}
							else
							{
								//event.preventDefault();
								bAdvance = false;
							}
							break;
						}
						case "A":
						{
							// if the character is an alphabet, 
							// convert it to upper case and accept it.
							if (isLetter(c))
							{
								allowChar(c.toUpperCase());
							}
							else
							{
								//event.preventDefault();
								bAdvance = false;
							}
							break;
						}
						case "a":
						{
							// if the character is an alphabet, 
							// convert it to lower case and accept it.
							if (isLetter(c))
							{
								allowChar(c.toLowerCase());
							}
							else
							{
								//event.preventDefault();
								bAdvance = false;
							}
							break;
						}
						case "B":
						{
							// if the character is an alphabet, accept it.
							if (isLetter(c))
							{
								allowChar(c);
							}
							else
							{
								//event.preventDefault();
								bAdvance = false;
							}
							break;
						}
						case ".":
						{
							// if the character is not a digit, accept it.
							if (isDigit(c))
							{
								//event.preventDefault();
								bAdvance = false;
							}
							else
							{
								allowChar(c);
							}
							break;
						}
						case "*":
						{
							// if the character is a digit or an alphabet, accept it.
							if (isDigit(c) || isLetter(c))
							{
								allowChar(c);
							}
							else
							{
								//event.preventDefault();
								bAdvance = false;
							}
							break;
						}
						default:
							break;
					}
					
					if (bAdvance)
					{
						// If embeded hints are displayed then update the
						// UITextField corresponding to the embeded hints
						if (dateField)
						{
							var s:String = embedTextField.text == null ? "" : embedTextField.text;
							
							if (s.length > 0)
							{
								s = s.substring(0, _position) + " " + s.substring(_position + 1, s.length);
								embedTextField.text = s;
							}
						}
						
						advancePosition();
					}
					else
					{
						if (event != null)
						{
							event.preventDefault();
						}
					}
					
					workingUpdated = true;
					
					invalidateDisplayList();
					
				}
			}
		}
		
		/**
		 *  @private
		 *  Handle TEXT_INPUT events by matching the character with
		 *  the mask and either blocking or allowing the character.
		 */
		private function interceptChar(event:TextEvent):void
		{
			// If the mask is incorrect or not spceified then treat the control
			// as a standard TextInput control
			if (defaultBehaviour)
			{
				return;
			}
			
			// Get the typed characters
			var input:String = event.text;
			
			if (_position >= actualLength)
			{
				event.preventDefault();
				
				// If autoAdvance flag is set, then set the focus to the
				// next control according to the tab index.
				if (_autoAdvance && !isWorkingIncomplete())
				{
					var obj:UIComponent = UIComponent(focusManager.getNextFocusManagerComponent());
					
					if ((obj is MaskedTextInput) || (obj is TextInput) || (obj is TextArea))
					{
						obj.setFocus();
					}
				}
				
				// Dispatch the inputMaskEnd Event
				dispatchEvent(new Event("inputMaskEnd"));
				return;
			}
			
			handleInput(input, event);
		}
		
		/**
		 *  @private
		 *  Consumes the FOCUS_IN and FOCUS_OUT event and repositions the insertion
		 *  point and perform additional checks based on the required property.
		 */
		private function interceptFocus(event:FocusEvent):void
		{
			if (defaultBehaviour)
			{
				return;
			}
			
			if (event.type == FocusEvent.FOCUS_IN)
			{
				focusByClick = true;
				// If tab is used to move within the fields of the control,
				// then position the insertion point to the begining of the
				// next group of mask characters
				_position = -1;
				
				// advance the insertion point to the first viable input field.
				advancePosition();
				// selects the current subfield
				changeSelection(0);
			}
			else if (event.type == FocusEvent.FOCUS_OUT)
			{
				textField.setColor(0x000000);
				
				if (_required && isWorkingIncomplete())
				{
					var errorTextColor:Number = getStyle("errorTextColor");
					
					if (errorTextColor)
					{
						textField.setColor(errorTextColor);
					}
					else
					{
						textField.setColor(0xFF0000);
					}
				}
			}
		
		}
		
		/**
		 *  @private
		 *  Handles key press event for special keys like Delete, Backspace, etc.
		 */
		private function interceptKey(event:flash.events.KeyboardEvent):void
		{
			if (defaultBehaviour)
			{
				super.keyDownHandler(event);
				return;
			}
			
			// Delete one character before the insertion point 
			// and moves the insertion point to one position back.
			if (event.keyCode == Keyboard.BACKSPACE)
			{
				_position = selectionBeginIndex;
				handleDeletions(true);
			}
			// Delete one character at the insertion point 
			else if (event.keyCode == Keyboard.DELETE)
			{
				handleDeletions();
			}
			// Moves the insertion point to the previous viable input position
			else if (event.keyCode == Keyboard.LEFT)
			{
				if (_position > 0 && _working[_position - 1] == " ")
				{
					setSelection(_position, _position);
				}
				else
				{
					_position = this.selectionBeginIndex;
					retreatPosition();
				}
				event.preventDefault();
			}
			// Moves the insertion point to the next viable input position
			else if (event.keyCode == Keyboard.RIGHT)
			{
				if (_position == actualLength - 1)
				{
					++_position;
					setSelection(_position, _position);
				}
				else if (_position < actualLength && _working[_position] == " ")
				{
					setSelection(_position, _position);
				}
				else
				{
					advancePosition();
				}
				
				event.preventDefault();
			}
			// Moves the insertion point to the last viable input position
			else if (event.keyCode == Keyboard.END)
			{
				var b:Boolean = false;
				
				for (var i:int = _position; i < actualLength; i++)
				{
					if (_working[i] == " ")
					{
						_position = i;
						setSelection(i, i);
						b = true;
						break;
					}
				}
				
				if (!b)
				{
					_position = _working.length;
				}
				event.preventDefault();
			}
			// Moves the insertion point to the first viable input position
			else if (event.keyCode == Keyboard.HOME)
			{
				_position = -1;
				advancePosition(true);
			}
			workingUpdated = true;
			invalidateDisplayList();
		}
		
		/**
		 *  @private
		 *  Returns true if the given character is a digit.
		 */
		private function isDigit(c:String):Boolean
		{
			return ((c >= "0" && c <= "9"));
		}
		
		/**
		 *  @private
		 *  Returns true if the given character is an Alphabet.
		 */
		private function isLetter(c:String):Boolean
		{
			return (((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")));
		}
		
		/**
		 *  @private
		 *  Returns true if the given character is a masking character.
		 */
		private function isMask(c:String):Boolean
		{
			return (c == "#" || c == "A" || c == "a" || c == "B" || c == "." || c == "*");
		}
		
		/**
		 *  @private
		 *  Returns true if the text field is left incomplete.
		 */
		private function isWorkingIncomplete(len:Number = -1):Boolean
		{
			if (len == -1)
			{
				len = actualLength;
			}
			
			for (var i:int = 0; i < len; i++)
			{
				if (_working[i] == " " && maskMap[i][1])
				{
					return true;
				}
			}
			return false;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Event Handlers
		//
		//--------------------------------------------------------------------------
		
		/**
		 *  @private
		 *  Handles cut and do nothing.
		 */
		private function menuHandler(event:Event):void
		{
			// In case of a standard text field, perform the default cut action
			if (defaultBehaviour)
			{
				return;
			}
			var str:String = super.text;
			super.text = _working.join("");
			event.preventDefault();
			return;
		}
		
		/**
		 *  @private
		 *  Handles TAB and SHIFT+TAB event and repositions the insertion point.
		 *  The insertion point(cursor) is moved to the position of the
		 *  next/previous subfield respectively.
		 */
		//commented to retain the default Tab and Shift-Tab behavior
		//uncomment if you want the focus to be shifted to the
		// next/previous sub-field in case of a Tab/Shift-Tab respectively.
		/* private function tabHandler(event:FocusEvent):void
		   {
		   // Move the insertion point to the previous group of mask characters
		   if(event.shiftKey && event.keyCode == Keyboard.TAB)
		   {
		   var foundMask:Boolean = false;
		   if(!(_position <=0))
		   {
		   for(var i:int=_position; i >= 0; i--)
		   {
		   if(foundMask && (!maskMap[i][1] || i==0))
		   {
		   if(!maskMap[i][1])
		   {
		   _position = i + 1;
		   setSelection(_position,_position);
		   event.preventDefault();
		   break;
		   }
		   if(i==0)
		   {
		   _position = 0;
		   setSelection(_position,_position);
		   event.preventDefault();
		   break;
		   }
		   }
		
		   if(!foundMask && (i>0 && i<actualLength) && !maskMap[i][1] && maskMap[i-1][1])
		   {
		   foundMask = true;
		   }
		
		   }
		   changeSelection(_position);
		   }
		   }
		   // Move the insertion point to the next group of mask characters
		   else if(event.keyCode == Keyboard.TAB)
		   {
		   if(_position != actualLength-1)
		   {
		   for(i=_position; i < actualLength; i++)
		   {
		   if(!maskMap[i][1] && maskMap[i+1][1])
		   {
		   _position = i + 1;
		   setSelection(_position,_position);
		   changeSelection(_position);
		   event.preventDefault();
		   break;
		   }
		   }
		   }
		   }
		 } */
		
		/**
		 *  @private
		 *  Handles MOUSE_CLICK event and repositions the insertion point.
		 *  The insertion point(cursor) is moved to the position of the
		 *  mouse click if all the characters in the subfield(to the left
		 *  of the click point) are filled.
		 */
		private function reposition(event:flash.events.MouseEvent):void
		{
			if (defaultBehaviour)
			{
				return;
			}
			
			// Adding MOUSE_UP event listener
			if (event.type == MouseEvent.MOUSE_DOWN)
			{
				event.preventDefault();
			}
			
			// Handles triple click
			var now:Number = getTimer();
			
			if (lastTime != 0 && (now - lastTime) < 300)
			{
				lastTime = 0;
				setSelection(0, actualLength);
				event.preventDefault();
				return;
			}
			
			// Changed so that cursor can not be positioned to a subfield
			// if any of the previous subfield is partially filled.
			//if(this.selectionBeginIndex <= actualLength 
			//	&& _working[this.selectionBeginIndex-1] == " ")
			if (this.selectionBeginIndex <= actualLength && isWorkingIncomplete(this.selectionEndIndex))
			{
				//if(maskMap[this.selectionBeginIndex-1][1])
				setSelection(_position, _position);
			}
			else
			{
				_position = this.selectionBeginIndex;
				
				if (focusByClick)
				{
					setSelection(_position, _position);
					focusByClick = false;
				}
			}
			event.preventDefault();
		}
		
		/**
		 *  @private
		 *  Moves the insertion point backward (if possible) to the previous
		 *  viable input position.
		 */
		private function retreatPosition():void
		{
			var p:Number = _position;
			var posChanged:Boolean = false;
			
			while ((--p) >= 0)
			{
				posChanged = true;
				
				if (p <= 0 || maskMap[p][1])
				{
					break;
				}
			}
			
			if (posChanged)
			{
				_position = p;
			}
			
			setSelection(_position, _position);
		}
	}
}